importPackage(Packages["okhttp3"]);

const comUtil = require("./commondUtils.js");
// 线程中断异常
const interruptErrorMsg = "com.stardust.autojs.runtime.exception.ScriptInterruptedException: null";

let url = null;
let options = {};

let okHttp3ClientObj = null;
let ws = null;

let heartbeatThread = null; // 心跳线程
let lastMessageTime = null; // 最后收到消息时间

let reconnectThread = null; // 心跳线程
let isReconnecting = null; // 是否正在重连
let reconnectCount = 0; // 重连次数

let isManualClose = null; // 是否手动关闭
let connectFlag = false; // 连接标识

const defaultOptions = {
    heartbeatIntervalTime: 60000, // 心跳间隔60秒
    // heartbeatTimeout: 150000, // 心跳超时150秒
    heartbeatMsg: JSON.stringify({"type": "heartbeat"}), // 心跳消息
    reconnectIntervalTime: 10000, // 重连初始间隔10秒
    reconnectMaxNum: 10, // 最大尝试重连次数
    onOpen: null,
    onMessage: null,
    onClosing: null,
    onClosed: null,
    onFailure: null,
};

const exportObj = {}

exportObj.connect = (u, o) => {
    if (comUtil.isBlank(u)) {
        return;
    }
    if (!u.includes("ws://") && !u.includes("wss://")) {
        u = "ws://" + u;
    }

    url = u;
    o = o || {};
    for (let key in defaultOptions) {
        if (o[key] == null) {
            o[key] = defaultOptions[key];
        }
    }
    options = o;

    connectWebsocket();
}

/**
 * 连接
 */
function connectWebsocket() {
    if (connectFlag) {
        console.warn("[WebsocketClient] 已连接，无需重复连接");
        return;
    }

    okHttp3ClientObj = new OkHttpClient.Builder().retryOnConnectionFailure(true).build();
    okHttp3ClientObj.dispatcher().cancelAll();
    ws = okHttp3ClientObj.newWebSocket(
        new Request.Builder().url(url).build(),
        new WebSocketListener({
            onOpen: (webSocket, response) => {
                connectFlag = true;
                reconnectCount = 0;
                lastMessageTime = Date.now();
                console.info('[WebsocketSingleClient] 连接成功', url);
                typeof options.onOpen == 'function' && options.onOpen(response);
                // 启动心跳线程
                startHeartbeat();
                // 关闭重连线程
                if (reconnectThread != null) {
                    reconnectThread.interrupt();
                    reconnectThread = null;
                }
            },
            onMessage: (webSocket, msg) => {
                // 最后收到消息时间
                lastMessageTime = Date.now();
                if (comUtil.isBlank(msg)) {
                    console.warn('[WebsocketClient] 收到的消息为空');
                    return;
                }
                // 忽略心跳消息
                if (msg.includes(options.heartbeatMsg)) {
                    return;
                }
                console.info('[WebsocketClient] 收到消息', msg);
                typeof options.onMessage == 'function' && options.onMessage(msg);
            },
            onClosing: (webSocket, code, reason) => {
                console.info('[WebsocketSingleClient] 正在关闭连接. code：%d, reason：%s', code, reason);
                connectFlag = false;
                // 停止心跳线程
                stopHeartbeat();
                // 关闭连接
                closeResource(reason);
                typeof options.onClosing == 'function' && options.onClosing(code, reason);
                if (reason != null && (reason.includes('断开旧的连接') || reason.includes('API请求关闭客户端连接'))) {
                    return;
                }
                // 重连 5s后
                threads.start(function () {
                    sleep(5000);
                    reconnect();
                });
            },
            onClosed: (webSocket, code, reason) => {
                console.info('[WebsocketSingleClient] 连接关闭. code：%d, reason：%s', code, reason);
                connectFlag = false;
                typeof options.onClosed == 'function' && options.onClosed(code, reason);
            },
            onFailure: (webSocket, e, reason) => {
                console.error('[WebsocketSingleClient] 连接异常! reason：%s', reason, e);
                connectFlag = false;
                typeof options.onFailure == 'function' && options.onFailure(e, reason);
                // 异常重连
                reconnect();
            }
        })
    );
    isReconnecting = false;
}

/**
 * 断线重连
 */
function reconnect() {
    if (isReconnecting || isManualClose) {
        return;
    }
    if (connectFlag && ws != null) {
        ws.close(1000, "断线重连前断开旧的连接");
        ws = null;
    }
    if (options.reconnectMaxNum > 0 && reconnectCount >= options.reconnectMaxNum) {
        console.warn("[WebsocketSingleClient] 超过重连最大次数...%d", options.reconnectMaxNum);
        isReconnecting = false;
        return;
    }
    isReconnecting = true;
    reconnectThread = threads.start(function () {
        reconnectCount++;
        // 最大重连间隔时间 5m
        const maxIntervalTime = 300000;
        // 指数退避：间隔时间逐渐增加（例如：10s, 20s, 40s...）
        const delay = Math.min(options.reconnectIntervalTime * Math.pow(2, reconnectCount), maxIntervalTime);
        console.info("[WebsocketSingleClient] %ds后进行重连...", delay / 1000);
        sleep(delay);
        console.info("[WebsocketSingleClient] 重连中...%d", reconnectCount);
        connectWebsocket(url, options);
    });
}

function closeResource(reason) {
    if (reconnectThread != null) {
        reconnectThread.interrupt();
        reconnectThread = null;
    }

    if (ws != null) {
        ws.close(1000, reason);
        ws = null;
    }

    if (okHttp3ClientObj != null) {
        okHttp3ClientObj.dispatcher().executorService().shutdown();
        okHttp3ClientObj = null;
    }
}

/**
 * 启动心跳
 */
function startHeartbeat() {
    stopHeartbeat();
    const g = this;
    heartbeatThread = threads.start(function () {
        while (true) {
            if (isReconnecting || !connectFlag) {
                heartbeatThread = null;
                break;
            }
            try {
                sleep(options.heartbeatIntervalTime);
                exportObj.sendMsg(options.heartbeatMsg);
            } catch (e) {
                if(e.message != interruptErrorMsg) {
                    console.error("发送心跳异常", JSON.stringify(e));
                }
                heartbeatThread = null;
                break;
            }
        }
    });
}

/**
 * 停止心跳
 */
function stopHeartbeat() {
    if (heartbeatThread != null) {
        heartbeatThread.interrupt();
        heartbeatThread = null;
    }
}

/**
 * 发送消息
 * @param {string|object} data 要发送的数据
 */
exportObj.sendMsg = (data) => {
    let msg = data;
    if (typeof msg !== 'string') {
        msg = JSON.stringify(msg);
    }
    if (ws == null || !connectFlag) {
        if (msg !== options.heartbeatMsg) {
            console.error('[WebsocketSingleClient] 服务端已断开，消息发送失败', msg);
        }
        return;
    }
    ws.send(msg);
}

/**
 * 关闭连接
 */
exportObj.closeWs = () => {
    isManualClose = true;
    closeResource("用户主动关闭websocket连接");
}

/**
 * 是否已连接
 * @returns {boolean}
 */
exportObj.isConnected = () => {
    return connectFlag && !isReconnecting;
}

// 脚本运行结束关闭所有连接
events.on("exit", function(){
    exportObj.closeWs("脚本运行结束关闭所有连接");
});

module.exports = exportObj

// let heartbeatCheckThread = null; // 心跳检测线程
//
// /**
//  * 启动心跳超时检查
//  */
// function startHeartbeatCheck() {
//     stopHeartbeatCheck();
//     heartbeatCheckThread = threads.start(function () {
//         while (true) {
//             if (isReconnecting || !connectFlag) {
//                 heartbeatCheckThread = null;
//                 break;
//             }
//             try {
//                 sleep(options.heartbeatTimeout);
//                 if ((Date.now() - lastMessageTime) > options.heartbeatTimeout) {
//                     console.warn('[WebsocketSingleClient] 心跳超时，开始重连...');
//                     reconnect();
//                 }
//             } catch (e) {
//                 if(e.message != interruptErrorMsg) {
//                     console.error("心跳超时重连异常!", JSON.stringify(e));
//                 }
//                 heartbeatCheckThread = null;
//                 break;
//             }
//         }
//     });
// }
//
// /**
//  * 停止心跳超时检查
//  */
// function stopHeartbeatCheck() {
//     if (heartbeatCheckThread != null) {
//         heartbeatCheckThread.interrupt();
//         heartbeatCheckThread = null;
//     }
// }